//
// Created on 2024/4/19.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#include "glesmain.h"
#include "common.h"
#include <native_vsync/native_vsync.h>
#include <string>
#include <ace/xcomponent/native_interface_xcomponent.h>


const EGLint ATTRIB_LIST[] = {
    // Key,value.
    EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
    // End.
    EGL_NONE};
const EGLint ATTRIB_LIST2[] = {
    // Key,value.
    EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
    // End.
    EGL_NONE};

const EGLint CONTEXT_ATTRIBS[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};


static void OnNativeVSync_FrameCallback(long long timestamp, void *data) {

    glesmain *glesmain_ = (glesmain *)data;
    glesmain_->OnFrame();
}
void glesmain::OnSurfaceCreateCB(void *component, void *window) {
    auto t = getThreadID();
    MLOGFormat("SurfaceCreate thread=%{public}16x", t);


    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NativeXComponent_GetXComponentId((OH_NativeXComponent *)component, idStr, &idSize) !=
        OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        MERROR("OnSurfaceCreatedCB: Unable to get XComponent id");
        return;
    }
    MLOGFormat("OH_NativeXComponent name=%{public}s", idStr);

    uint64_t width;
    uint64_t height;
    int32_t xSize = OH_NativeXComponent_GetXComponentSize((OH_NativeXComponent *)component, window, &width, &height);
    MLOGFormat("size thread=%{public}d %{public}d", width, height);

    MLOG("EglContextInit execute");
    if ((window == nullptr) || (width <= 0) || (height <= 0)) {
        MERROR("EglContextInit: param error");
        return;
    }
    m_egl_window = (EGLNativeWindowType)(window);

    //    // Init display.
    m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if (m_eglDisplay == EGL_NO_DISPLAY) {
        MERROR("eglGetDisplay: unable to get EGL display");
        return;
    }
    //
    {
        EGLint majorVersion;
        EGLint minorVersion;
        if (!eglInitialize(m_eglDisplay, &majorVersion, &minorVersion)) {
            MERROR("eglInitialize: unable to get initialize EGL display");
            return;
        }

        int err = eglGetError();
        MLOGFormat("eglInitialize %{public}d %{public}d err %{public}04x", majorVersion, minorVersion, err);
    }
    //    // Select configuration.
    EGLint numConfigs;
    { // 研究数量


        if (!eglGetConfigs(m_eglDisplay, nullptr, 0, &numConfigs)) {
            MERROR("eglChooseConfig: unable to choose configs");
            return;
        }
        MLOGFormat("eglChooseConfig max= %{public}d ", numConfigs);

        EGLConfig *configs = new EGLConfig[numConfigs];
        for (int i = 0; i < numConfigs; i++) {
            configs[i] = 0;
        }

        int num = numConfigs;
        if (!eglGetConfigs(m_eglDisplay, configs, num, &numConfigs)) {
            MERROR("eglChooseConfig: unable to choose configs");
            return;
        }
        for (int i = 0; i < numConfigs; i++) {
            MLOGFormat("eglChooseConfig %{public}d == %{public}d ", i, configs[i]);
        }


        delete[] configs;
    }
    bool bconfig = eglChooseConfig(m_eglDisplay, ATTRIB_LIST, &m_eglConfig, 1, &numConfigs);
    if(!bconfig)
    {
        MERROR("什么破设备GLES3都初始化不出来.");
        bconfig = eglChooseConfig(m_eglDisplay, ATTRIB_LIST2, &m_eglConfig, 1, &numConfigs);
        if(!bconfig)
        {
            MERROR("GLES2都初始化不出来,毁灭吧");
            return;
        }
    }
    
    // Create surface.
    int err2 = eglGetError();
    MLOGFormat("eglChooseConfig %{public}d %{public}04x", m_eglConfig, err2);
    EGLint winAttribs[] = {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, EGL_NONE};
    m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, m_egl_window, nullptr);
    if (m_eglSurface == nullptr) {
        int err3 = eglGetError();
        // #define EGL_BAD_ATTRIBUTE 0x3004
        MERRORFormat("eglCreateWindowSurface %{public}d %{public}04x", m_eglSurface, err3);
        // MERROR("eglCreateWindowSurface: unable to create surface");
        return;
    }
    {
        // Create context.
        m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
        int err4 = eglGetError();
        MLOGFormat("eglCreateContext %{public}d %{public}04x", m_eglContext, err4);

        if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) {
            int err = eglGetError();
            auto errstr = eglQueryString(m_eglDisplay, err);
            // 3006 EGL_BAD_CONTEXT
            MERRORFormat("eglMakeCurrent failed %{public}04x %{public}s", err, errstr);
            return;
        }
        
        const char *ptrver = (const char *)glGetString(GL_VERSION);
        MLOGFormat("GLVersion %{public}s", ptrver);
        // Create program.
        //    program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER);
        //    if (program_ == PROGRAM_ERROR) {
        //        MERROR("CreateProgram: unable to create program");
        //        return ;
        //    }
        glViewport(0, 0, width, height);
        
        glClearColor(0.8f, 0.1f, 0.8f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glFlush();
        glFinish();
        eglSwapBuffers(m_eglDisplay, m_eglSurface);
    }

    std::string name = "render_vsync";
    this->m_sync = OH_NativeVSync_Create(name.c_str(), name.length());
    OH_NativeVSync_RequestFrame(this->m_sync, OnNativeVSync_FrameCallback, this);
}
void glesmain::OnFrame() {
    // 绘制一帧
    uint64_t tid = getThreadID();
    MLOGFormat("VSync thread=%{public}16x", tid);

//    if (m_eglContext == nullptr) {
//        // Create context.
//        m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
//        int err4 = eglGetError();
//        MLOGFormat("eglCreateContext %{public}d %{public}04x", m_eglContext, err4);
//
//        if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) {
//            int err = eglGetError();
//            auto errstr = eglQueryString(m_eglDisplay, err);
//            // 3006 EGL_BAD_CONTEXT
//            MERRORFormat("eglMakeCurrent failed %{public}04x %{public}s", err, errstr);
//            return;
//        }
//
//        const char *ptrver = (const char *)glGetString(GL_VERSION);
//        MLOGFormat("GLVersion %{public}s", ptrver);
//        // Create program.
//        //    program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER);
//        //    if (program_ == PROGRAM_ERROR) {
//        //        MERROR("CreateProgram: unable to create program");
//        //        return ;
//        //    }
//        glViewport(0, 0, 100, 100);
//        glClear(GL_COLOR_BUFFER_BIT);
//        glClearColor(0.8f, 0.1f, 0.8f, 1.0f);
//        glFlush();
//        glFinish();
//
//    } else {
//        glClear(GL_COLOR_BUFFER_BIT);
//        glClearColor(0.8f, 0.1f, 0.8f, 1.0f);
//        glFlush();
//    }
//
//    OH_NativeVSync_RequestFrame(this->m_sync, OnNativeVSync_FrameCallback, this);
}